﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace TFDP.Common
{
    public class Logger
    {
        #region Singleton Implementation
        private static object lockObject = new object();
        private static Logger instance;

        public static Logger Instance
        {
            get
            {
                return instance;
            }
        }

        public static void Initialize()
        {
            Logger.CreateInstance(TraceLevel.Warning);
        }

        public static void CreateInstance(TraceLevel level)
        {
            if (instance == null)
                lock (lockObject)
                    if (instance == null)
                        instance = new Logger(TraceLevel.Warning);
        }

        public static void Write(TraceLevel level, string message)
        {
            Logger.Instance.Log(level, message);
        }

        public static void Write(TraceLevel level, string format, params object[] args)
        {
            Logger.Instance.Log(level, String.Format(format, args));
        }

        public static void LogError(string message)
        {
            Logger.Instance.Log(TraceLevel.Error, message);
        }

        public static void LogWarning(string message)
        {
            Logger.Instance.Log(TraceLevel.Warning, message);
        }

        public static void LogInfo(string message)
        {
            Logger.Instance.Log(TraceLevel.Info, message);
        }

        public static void LogVerbose(string message)
        {
            Logger.Instance.Log(TraceLevel.Verbose, message);
        }

        public static void LogExtended(TraceLevel level, string message, string document, int line, int column)
        {
            Logger.Instance.LogWithInfo(level, message, document, line, column);
        }
        #endregion

        private TraceLevel outputLevel;

        private Logger(TraceLevel outputLevel)
        {
            this.outputLevel = outputLevel;
        }

        public void Log(TraceLevel level, string message)
        {
            if (level <= outputLevel)
            {
                // This is only for internal logging in debug mode.
                // Further Logger output should be exported using the
                // LogMessage event.
                Trace.WriteLine(message);

                // Raise the event
                RaiseNewMessage(level, message);
            }
        }

        public void LogWithInfo(TraceLevel level, string message, string document, int line, int column)
        {
            LoggerEventArgs e = new LoggerEventArgs(
                level,
                message,
                new Dictionary<string, string>() {
                    { LoggerEventArgs.Document, document }, 
                    { LoggerEventArgs.Line, line.ToString() }, 
                    { LoggerEventArgs.Column, column.ToString() } 
                }
            );

            RaiseNewMessage(e);
        }

        public event EventHandler<LoggerEventArgs> NewMessage;

        private void RaiseNewMessage(TraceLevel level, string message)
        {
            if (NewMessage != null)
                NewMessage(this, new LoggerEventArgs(level, message));
        }

        private void RaiseNewMessage(LoggerEventArgs e)
        {
            if (NewMessage != null)
                NewMessage(this, e);
        }
    }

    public class LoggerEventArgs : EventArgs
    {
        public const string Document = "document";
        public const string Line = "line";
        public const string Column = "column";

        #region Members
        private TraceLevel level;
        private string message;
        private Dictionary<string, string> extraInfo;
        #endregion

        #region Properties
        public TraceLevel Level
        {
            get { return level; }
        }
        public string Message
        {
            get { return message; }
        }
        public Dictionary<string, string> ExtraInfo
        {
            get { return extraInfo; }
        }
        public string this[string key]
        {
            get { return extraInfo[key]; }
        }
        #endregion

        #region Constructors
        public LoggerEventArgs(TraceLevel level, string message)
            : base()
        {
            this.level = level;
            this.message = message;
            this.extraInfo = null;
        }

        public LoggerEventArgs(TraceLevel level, string message, Dictionary<string, string> extraInfo)
            : base()
        {
            this.level = level;
            this.message = message;
            this.extraInfo = extraInfo;
        }
        #endregion
    }
}
